home *** CD-ROM | disk | FTP | other *** search
- Subject: v16i032: Less, a pager that's more than more, Part03/04
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: ctnews!UNIX386!mark
- Posting-number: Volume 16, Issue 32
- Archive-name: less5/part03
-
- #! /bin/sh
- # This is a shell archive.
- # Remove anything before this line, then unpack it
- # by saving it into a file and typing "sh file".
-
- echo shar: Extracting \"main.c\"
- sed "s/^X//" >'main.c' <<'END_OF_FILE'
- X/*
- X * Entry point, initialization, miscellaneous routines.
- X */
- X
- X#include "less.h"
- X#include "position.h"
- X
- Xpublic int ispipe;
- Xpublic char * first_cmd;
- Xpublic char * every_first_cmd;
- Xpublic int new_file;
- Xpublic int is_tty;
- Xpublic char *current_file;
- Xpublic char *previous_file;
- Xpublic POSITION prev_pos;
- Xpublic int any_display;
- Xpublic int scroll;
- Xpublic int ac;
- Xpublic char ** av;
- Xpublic int curr_ac;
- Xpublic int quitting;
- X
- Xextern int file;
- Xextern int quit_at_eof;
- Xextern int cbufs;
- Xextern int errmsgs;
- X
- X#if LOGFILE
- Xpublic int logfile = -1;
- Xpublic int force_logfile = 0;
- Xpublic char * namelogfile = NULL;
- X#endif
- X
- X#if EDITOR
- Xpublic char * editor;
- X#endif
- X
- X#if TAGS
- Xextern char * tagfile;
- Xextern char * tagpattern;
- Xextern int tagoption;
- X#endif
- X
- X
- X/*
- X * Edit a new file.
- X * Filename "-" means standard input.
- X * No filename means the "current" file, from the command line.
- X */
- X public void
- Xedit(filename)
- X register char *filename;
- X{
- X register int f;
- X register char *m;
- X POSITION initial_pos;
- X char message[100];
- X static int didpipe;
- X
- X initial_pos = NULL_POSITION;
- X if (filename == NULL || *filename == '\0')
- X {
- X if (curr_ac >= ac)
- X {
- X error("No current file");
- X return;
- X }
- X filename = save(av[curr_ac]);
- X } else if (strcmp(filename, "#") == 0)
- X {
- X if (*previous_file == '\0')
- X {
- X error("no previous file");
- X return;
- X }
- X filename = save(previous_file);
- X initial_pos = prev_pos;
- X } else
- X filename = save(filename);
- X
- X if (strcmp(filename, "-") == 0)
- X {
- X /*
- X * Use standard input.
- X */
- X if (didpipe)
- X {
- X error("Can view standard input only once");
- X return;
- X }
- X f = 0;
- X } else if ((m = bad_file(filename, message, sizeof(message))) != NULL)
- X {
- X error(m);
- X free(filename);
- X return;
- X } else if ((f = open(filename, 0)) < 0)
- X {
- X error(errno_message(filename, message, sizeof(message)));
- X free(filename);
- X return;
- X }
- X
- X if (isatty(f))
- X {
- X /*
- X * Not really necessary to call this an error,
- X * but if the control terminal (for commands)
- X * and the input file (for data) are the same,
- X * we get weird results at best.
- X */
- X error("Can't take input from a terminal");
- X if (f > 0)
- X close(f);
- X free(filename);
- X return;
- X }
- X
- X#if LOGFILE
- X if (f == 0 && namelogfile != NULL && is_tty)
- X use_logfile();
- X#endif
- X
- X /*
- X * We are now committed to using the new file.
- X * Close the current input file and set up to use the new one.
- X */
- X if (file > 0)
- X close(file);
- X new_file = 1;
- X if (previous_file != NULL)
- X free(previous_file);
- X previous_file = current_file;
- X current_file = filename;
- X prev_pos = position(TOP);
- X ispipe = (f == 0);
- X if (ispipe)
- X didpipe = 1;
- X file = f;
- X ch_init(cbufs, 0);
- X init_mark();
- X
- X if (every_first_cmd != NULL)
- X first_cmd = every_first_cmd;
- X
- X if (is_tty)
- X {
- X int no_display = !any_display;
- X any_display = 1;
- X if (no_display && errmsgs > 0)
- X {
- X /*
- X * We displayed some messages on error output
- X * (file descriptor 2; see error() function).
- X * Before erasing the screen contents,
- X * display the file name and wait for a keystroke.
- X */
- X error(filename);
- X }
- X /*
- X * Indicate there is nothing displayed yet.
- X */
- X pos_clear();
- X if (initial_pos != NULL_POSITION)
- X jump_loc(initial_pos);
- X clr_linenum();
- X }
- X}
- X
- X/*
- X * Edit the next file in the command line list.
- X */
- X public void
- Xnext_file(n)
- X int n;
- X{
- X if (curr_ac + n >= ac)
- X {
- X if (quit_at_eof)
- X quit();
- X error("No (N-th) next file");
- X } else
- X edit(av[curr_ac += n]);
- X}
- X
- X/*
- X * Edit the previous file in the command line list.
- X */
- X public void
- Xprev_file(n)
- X int n;
- X{
- X if (curr_ac - n < 0)
- X error("No (N-th) previous file");
- X else
- X edit(av[curr_ac -= n]);
- X}
- X
- X/*
- X * Copy a file directly to standard output.
- X * Used if standard output is not a tty.
- X */
- X static void
- Xcat_file()
- X{
- X register int c;
- X
- X while ((c = ch_forw_get()) != EOI)
- X putchr(c);
- X flush();
- X}
- X
- X#if LOGFILE
- X
- Xuse_logfile()
- X{
- X int exists;
- X int answer;
- X char message[100];
- X
- X /*
- X * If he asked for a log file and we have opened standard input,
- X * create the log file.
- X * We take care not to blindly overwrite an existing file.
- X */
- X end_logfile();
- X
- X /*
- X * {{ We could use access() here. }}
- X */
- X exists = open(namelogfile, 0);
- X close(exists);
- X exists = (exists >= 0);
- X
- X if (exists && !force_logfile)
- X {
- X static char w[] = "WARNING: log file exists: ";
- X strcpy(message, w);
- X strtcpy(message+sizeof(w)-1, namelogfile,
- X sizeof(message)-sizeof(w));
- X error(message);
- X answer = 'X'; /* Ask the user what to do */
- X } else
- X answer = 'O'; /* Create the log file */
- X
- Xloop:
- X switch (answer)
- X {
- X case 'O': case 'o':
- X logfile = creat(namelogfile, 0644);
- X break;
- X case 'A': case 'a':
- X logfile = open(namelogfile, 1);
- X if (lseek(logfile, (offset_t)0, 2) < 0)
- X {
- X close(logfile);
- X logfile = -1;
- X }
- X break;
- X case 'D': case 'd':
- X answer = 0; /* Don't print an error message */
- X break;
- X case 'q':
- X quit();
- X default:
- X putstr("\n Overwrite, Append, or Don't log? ");
- X answer = getchr();
- X putstr("\n");
- X flush();
- X goto loop;
- X }
- X
- X if (logfile < 0 && answer != 0)
- X {
- X sprintf(message, "Cannot write to \"%s\"",
- X namelogfile);
- X error(message);
- X }
- X}
- X
- X#endif
- X
- X/*
- X * Entry point.
- X */
- Xmain(argc, argv)
- X int argc;
- X char *argv[];
- X{
- X char *getenv();
- X
- X
- X /*
- X * Process command line arguments and LESS environment arguments.
- X * Command line arguments override environment arguments.
- X */
- X init_prompt();
- X init_option();
- X scan_option(getenv("LESS"));
- X argv++;
- X while ( (--argc > 0) &&
- X (argv[0][0] == '-' || argv[0][0] == '+') &&
- X argv[0][1] != '\0')
- X scan_option(*argv++);
- X
- X#if EDITOR
- X editor = getenv("EDITOR");
- X if (editor == NULL || *editor == '\0')
- X editor = EDIT_PGM;
- X#endif
- X
- X /*
- X * Set up list of files to be examined.
- X */
- X ac = argc;
- X av = argv;
- X curr_ac = 0;
- X
- X /*
- X * Set up terminal, etc.
- X */
- X is_tty = isatty(1);
- X if (!is_tty)
- X {
- X /*
- X * Output is not a tty.
- X * Just copy the input file(s) to output.
- X */
- X if (ac < 1)
- X {
- X edit("-");
- X cat_file();
- X } else
- X {
- X do
- X {
- X edit((char *)NULL);
- X if (file >= 0)
- X cat_file();
- X } while (++curr_ac < ac);
- X }
- X exit(0);
- X }
- X
- X raw_mode(1);
- X get_term();
- X open_getchr();
- X init();
- X init_cmd();
- X
- X init_signals(1);
- X
- X /*
- X * Select the first file to examine.
- X */
- X#if TAGS
- X if (tagoption)
- X {
- X /*
- X * A -t option was given.
- X * Verify that no filenames were also given.
- X * Edit the file selected by the "tags" search,
- X * and search for the proper line in the file.
- X */
- X if (ac > 0)
- X {
- X error("No filenames allowed with -t option");
- X quit();
- X }
- X if (tagfile == NULL)
- X quit();
- X edit(tagfile);
- X if (file < 0)
- X quit();
- X if (tagsearch())
- X quit();
- X } else
- X#endif
- X if (ac < 1)
- X edit("-"); /* Standard input */
- X else
- X {
- X /*
- X * Try all the files named as command arguments.
- X * We are simply looking for one which can be
- X * opened without error.
- X */
- X do
- X {
- X edit((char *)NULL);
- X } while (file < 0 && ++curr_ac < ac);
- X }
- X
- X if (file >= 0)
- X commands();
- X quit();
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * Copy a string, truncating to the specified length if necessary.
- X * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
- X */
- X public void
- Xstrtcpy(to, from, len)
- X char *to;
- X char *from;
- X unsigned int len;
- X{
- X strncpy(to, from, len);
- X to[len-1] = '\0';
- X}
- X
- X/*
- X * Copy a string to a "safe" place
- X * (that is, to a buffer allocated by calloc).
- X */
- X public char *
- Xsave(s)
- X char *s;
- X{
- X register char *p;
- X
- X p = calloc(strlen(s)+1, sizeof(char));
- X if (p == NULL)
- X {
- X error("cannot allocate memory");
- X quit();
- X }
- X strcpy(p, s);
- X return (p);
- X}
- X
- X/*
- X * Exit the program.
- X */
- X public void
- Xquit()
- X{
- X /*
- X * Put cursor at bottom left corner, clear the line,
- X * reset the terminal modes, and exit.
- X */
- X quitting = 1;
- X#if LOGFILE
- X end_logfile();
- X#endif
- X lower_left();
- X clear_eol();
- X deinit();
- X flush();
- X raw_mode(0);
- X exit(0);
- X}
- END_OF_FILE
- echo shar: Extracting \"option.c\"
- sed "s/^X//" >'option.c' <<'END_OF_FILE'
- X/*
- X * Process command line options.
- X * Each option is a single letter which controls a program variable.
- X * The options have defaults which may be changed via
- X * the command line option, or toggled via the "-" command.
- X */
- X
- X#include "less.h"
- X
- X#define toupper(c) ((c)-'a'+'A')
- X
- X#define END_OPTION_STRING ('$')
- X
- X/*
- X * Types of options.
- X */
- X#define BOOL 01 /* Boolean option: 0 or 1 */
- X#define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */
- X#define NUMBER 04 /* Numeric option */
- X#define REPAINT 040 /* Repaint screen after toggling option */
- X#define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */
- X
- X/*
- X * Variables controlled by command line options.
- X */
- Xpublic int clean_data; /* Can we assume the data is "clean"?
- X (That is, free of nulls, etc) */
- Xpublic int quiet; /* Should we suppress the audible bell? */
- Xpublic int how_search; /* Where should forward searches start? */
- Xpublic int top_scroll; /* Repaint screen from top?
- X (alternative is scroll from bottom) */
- Xpublic int pr_type; /* Type of prompt (short, medium, long) */
- Xpublic int bs_mode; /* How to process backspaces */
- Xpublic int know_dumb; /* Don't complain about dumb terminals */
- Xpublic int quit_at_eof; /* Quit after hitting end of file twice */
- Xpublic int squeeze; /* Squeeze multiple blank lines into one */
- Xpublic int tabstop; /* Tab settings */
- Xpublic int back_scroll; /* Repaint screen on backwards movement */
- Xpublic int twiddle; /* Display "~" for lines after EOF */
- Xpublic int caseless; /* Do "caseless" searches */
- Xpublic int linenums; /* Use line numbers */
- Xpublic int cbufs; /* Current number of buffers */
- Xpublic int autobuf;
- Xpublic int plusoption;
- X
- Xextern char *prproto[];
- Xextern char *eqproto;
- Xextern int nbufs;
- Xextern int sc_window;
- Xextern int ispipe;
- Xextern char *first_cmd;
- Xextern char *every_first_cmd;
- X#if LOGFILE
- Xextern char *namelogfile;
- Xextern int force_logfile;
- Xextern int logfile;
- X#endif
- X#if TAGS
- Xextern char *tagfile;
- Xextern char *tagpattern;
- Xpublic int tagoption = 0;
- X#endif
- X
- Xstatic char *opt_P();
- X
- Xstatic struct option
- X{
- X char oletter; /* The controlling letter (a-z) */
- X char otype; /* Type of the option */
- X int odefault; /* Default value */
- X int *ovar; /* Pointer to the associated variable */
- X char *odesc[3]; /* Description of each value */
- X} option[] =
- X{
- X { 'a', TRIPLE, 0, &how_search,
- X { "Forward search starts at second REAL line displayed",
- X "Forward search starts at bottom of screen",
- X "Forward search starts at second SCREEN line displayed"
- X }
- X },
- X { 'b', NUMBER, 10, &cbufs,
- X { "%d buffers",
- X NULL, NULL
- X }
- X },
- X { 'B', BOOL, 1, &autobuf,
- X { "Don't automatically allocate buffers",
- X "Automatically allocate buffers when needed",
- X NULL
- X }
- X },
- X { 'c', TRIPLE, 0, &top_scroll,
- X { "Repaint by scrolling from bottom of screen",
- X "Repaint by clearing each line",
- X "Repaint by painting from top of screen"
- X }
- X },
- X { 'd', BOOL|NO_TOGGLE, 0, &know_dumb,
- X { NULL, NULL, NULL}
- X },
- X { 'e', TRIPLE, 0, &quit_at_eof,
- X { "Don't quit at end-of-file",
- X "Quit at end-of-file",
- X "Quit immediately at end-of-file"
- X }
- X },
- X { 'h', NUMBER, -1, &back_scroll,
- X { "Backwards scroll limit is %d lines",
- X NULL, NULL
- X }
- X },
- X { 'i', BOOL, 0, &caseless,
- X { "Case is significant in searches",
- X "Ignore case in searches",
- X NULL
- X }
- X },
- X { 'm', TRIPLE, 0, &pr_type,
- X { "Short prompt",
- X "Medium prompt",
- X "Long prompt"
- X }
- X },
- X { 'n', BOOL, 1, &linenums,
- X { "Don't use line numbers",
- X "Use line numbers",
- X NULL
- X }
- X },
- X { 'q', TRIPLE, 0, &quiet,
- X { "Ring the bell for errors AND at eof/bof",
- X "Ring the bell for errors but not at eof/bof",
- X "Never ring the bell"
- X }
- X },
- X { 's', BOOL|REPAINT, 0, &squeeze,
- X { "Don't squeeze multiple blank lines",
- X "Squeeze multiple blank lines",
- X NULL
- X }
- X },
- X { 'u', TRIPLE|REPAINT, 0, &bs_mode,
- X { "Underlined text displayed in underline mode",
- X "Backspaces cause overstrike",
- X "Backspaces print as ^H"
- X }
- X },
- X { 'w', BOOL|REPAINT, 1, &twiddle,
- X { "Display nothing for lines after end-of-file",
- X "Display ~ for lines after end-of-file",
- X NULL
- X }
- X },
- X { 'x', NUMBER|REPAINT, 8, &tabstop,
- X { "Tab stops every %d spaces",
- X NULL, NULL
- X }
- X },
- X { 'z', NUMBER|REPAINT, -1, &sc_window,
- X { "Scroll window size is %d lines",
- X NULL, NULL
- X }
- X },
- X { '\0' }
- X};
- X
- X/*
- X * Initialize each option to its default value.
- X */
- X public void
- Xinit_option()
- X{
- X register struct option *o;
- X
- X first_cmd = every_first_cmd = NULL;
- X
- X for (o = option; o->oletter != '\0'; o++)
- X {
- X /*
- X * Set each variable to its default.
- X */
- X *(o->ovar) = o->odefault;
- X }
- X}
- X
- X/*
- X * Toggle command line flags from within the program.
- X * Used by the "-" and "_" commands.
- X * If do_toggle is zero, just report the current setting, without changing it.
- X */
- X public void
- Xtoggle_option(s, do_toggle)
- X char *s;
- X int do_toggle;
- X{
- X int c;
- X register struct option *o;
- X char *msg;
- X int n;
- X int dorepaint;
- X char message[100];
- X
- X c = *s++;
- X
- X switch (c)
- X {
- X case 'P':
- X /*
- X * Special case for -P.
- X */
- X if (*s == '\0')
- X error(prproto[pr_type]);
- X else
- X (void) opt_P(s);
- X return;
- X#if TAGS
- X case 't':
- X /*
- X * Special case for -t.
- X */
- X if (*s == '\0')
- X {
- X error("no tag");
- X return;
- X }
- X findtag(s);
- X if (tagfile != NULL)
- X {
- X edit(tagfile);
- X (void) tagsearch();
- X }
- X return;
- X#endif
- X#if LOGFILE
- X case 'L':
- X /*
- X * Special case for -l and -L.
- X */
- X force_logfile = 1;
- X goto case_l;
- X case 'l':
- X force_logfile = 0;
- X case_l:
- X if (*s == '\0')
- X {
- X if (logfile < 0)
- X error("no log file");
- X else
- X {
- X sprintf(message, "log file \"%s\"",
- X namelogfile);
- X error(message);
- X }
- X return;
- X }
- X if (!ispipe)
- X {
- X error("input is not a pipe");
- X return;
- X }
- X if (logfile >= 0)
- X {
- X error("log file is already in use");
- X return;
- X }
- X namelogfile = save(s);
- X use_logfile();
- X sync_logfile();
- X return;
- X#endif
- X }
- X
- X msg = NULL;
- X for (o = option; o->oletter != '\0'; o++)
- X {
- X if (o->otype & NO_TOGGLE)
- X continue;
- X dorepaint = (o->otype & REPAINT);
- X if ((o->otype & BOOL) && (o->oletter == c))
- X {
- X /*
- X * Boolean option:
- X * just toggle it.
- X */
- X if (do_toggle)
- X *(o->ovar) = ! *(o->ovar);
- X } else if ((o->otype & TRIPLE) && (o->oletter == c))
- X {
- X /*
- X * Triple-valued option with lower case letter:
- X * make it 1 unless already 1, then make it 0.
- X */
- X if (do_toggle)
- X *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1;
- X } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
- X {
- X /*
- X * Triple-valued option with upper case letter:
- X * make it 2 unless already 2, then make it 0.
- X */
- X if (do_toggle)
- X *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2;
- X } else if ((o->otype & NUMBER) && (o->oletter == c))
- X {
- X n = getnum(&s, '\0');
- X if (n < 0)
- X {
- X /*
- X * No number; just a query.
- X * No need to repaint screen.
- X */
- X dorepaint = 0;
- X } else
- X {
- X /*
- X * Number follows the option letter.
- X * Set the variable to that number.
- X */
- X if (do_toggle)
- X *(o->ovar) = n;
- X }
- X
- X /*
- X * Special case for -b.
- X * Call ch_init to set new number of buffers.
- X */
- X if (o->ovar == &cbufs)
- X ch_init(cbufs, 1);
- X
- X sprintf(message, o->odesc[0],
- X (o->ovar == &back_scroll) ?
- X get_back_scroll() : *(o->ovar));
- X msg = message;
- X } else
- X continue;
- X
- X /*
- X * Print a message describing the new setting.
- X */
- X if (msg == NULL)
- X msg = o->odesc[*(o->ovar)];
- X error(msg);
- X
- X if (do_toggle && dorepaint)
- X repaint();
- X return;
- X }
- X
- X if (control_char(c))
- X sprintf(message, "-^%c", carat_char(c));
- X else
- X sprintf(message, "-%c", c);
- X strcat(message, ": no such flag.");
- X error(message);
- X}
- X
- X/*
- X * Determine if an option is a single character option (BOOL or TRIPLE),
- X * or if it a multi-character option (NUMBER).
- X */
- X public int
- Xsingle_char_option(c)
- X int c;
- X{
- X register struct option *o;
- X
- X if (c == 'P')
- X return (0);
- X#if TAGS
- X if (c == 't')
- X return (0);
- X#endif
- X#if LOGFILE
- X if (c == 'l' || c == 'L')
- X return (0);
- X#endif
- X for (o = option; o->oletter != '\0'; o++)
- X if (o->oletter == c)
- X return (o->otype & (BOOL|TRIPLE));
- X return (1);
- X}
- X
- X/*
- X * Scan to end of string or to an END_OPTION_STRING character.
- X * In the latter case, replace the char with a null char.
- X * Return a pointer to the remainder of the string, if any.
- X */
- X static char *
- Xoptstring(s, c)
- X char *s;
- X int c;
- X{
- X register char *p;
- X char message[80];
- X
- X if (*s == '\0')
- X {
- X sprintf(message, "string is required after -%c", c);
- X error(message);
- X exit(1);
- X }
- X for (p = s; *p != '\0'; p++)
- X if (*p == END_OPTION_STRING)
- X {
- X *p = '\0';
- X return (p+1);
- X }
- X return (p);
- X}
- X
- X/*
- X * Scan an argument (either from command line or from LESS environment
- X * variable) and process it.
- X */
- X public void
- Xscan_option(s)
- X char *s;
- X{
- X register struct option *o;
- X register int c;
- X int set_default;
- X char message[80];
- X
- X if (s == NULL)
- X return;
- X
- X set_default = 0;
- X next:
- X if (*s == '\0')
- X return;
- X switch (c = *s++)
- X {
- X case ' ':
- X case '\t':
- X case END_OPTION_STRING:
- X goto next;
- X case '-':
- X if (set_default = (*s == '+'))
- X s++;
- X goto next;
- X case '+':
- X plusoption = 1;
- X if (*s == '+')
- X every_first_cmd = save(++s);
- X first_cmd = s;
- X s = optstring(s, c);
- X goto next;
- X#if LOGFILE
- X case 'L':
- X force_logfile = 1;
- X /* FALLTHRU */
- X case 'l':
- X namelogfile = s;
- X s = optstring(s, c);
- X goto next;
- X#endif
- X#if TAGS
- X case 't':
- X {
- X char *p;
- X tagoption = 1;
- X p = s;
- X s = optstring(s, c);
- X findtag(p);
- X goto next;
- X }
- X#endif
- X case 'P':
- X s = opt_P(s);
- X goto next;
- X case '0': case '1': case '2': case '3': case '4':
- X case '5': case '6': case '7': case '8': case '9':
- X /*
- X * Handle special "more" compatibility form "-number"
- X * (instead of -znumber) to set the scrolling window size.
- X */
- X s--;
- X c = 'z';
- X break;
- X }
- X
- X for (o = option; o->oletter != '\0'; o++)
- X {
- X if ((o->otype & BOOL) && (o->oletter == c))
- X {
- X if (set_default)
- X *(o->ovar) = o->odefault;
- X else
- X *(o->ovar) = ! o->odefault;
- X goto next;
- X } else if ((o->otype & TRIPLE) && (o->oletter == c))
- X {
- X if (set_default)
- X *(o->ovar) = o->odefault;
- X else
- X *(o->ovar) = (o->odefault == 1) ? 0 : 1;
- X goto next;
- X } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
- X {
- X if (set_default)
- X *(o->ovar) = o->odefault;
- X else
- X *(o->ovar) = (o->odefault == 2) ? 0 : 2;
- X goto next;
- X } else if ((o->otype & NUMBER) && (o->oletter == c))
- X {
- X *(o->ovar) = getnum(&s, c);
- X goto next;
- X }
- X }
- X
- X sprintf(message, "\"-%c\": invalid flag", c);
- X error(message);
- X exit(1);
- X}
- X
- X/*
- X * Special case for -P.
- X */
- X static char *
- Xopt_P(s)
- X register char *s;
- X{
- X register char *es;
- X register char **proto;
- X
- X es = optstring(s, 'P');
- X
- X /*
- X * Figure out which prototype string should be changed.
- X */
- X switch (*s)
- X {
- X case 'm': proto = &prproto[PR_MEDIUM]; s++; break;
- X case 'M': proto = &prproto[PR_LONG]; s++; break;
- X case '=': proto = &eqproto; s++; break;
- X default: proto = &prproto[pr_type]; break;
- X }
- X
- X free(*proto);
- X *proto = save(s);
- X
- X return (es);
- X}
- X
- X/*
- X * Translate a string into a number.
- X * Like atoi(), but takes a pointer to a char *, and updates
- X * the char * to point after the translated number.
- X */
- X public int
- Xgetnum(sp, c)
- X char **sp;
- X int c;
- X{
- X register char *s;
- X register int n;
- X char message[80];
- X
- X s = *sp;
- X if (*s < '0' || *s > '9')
- X {
- X if (c == '\0')
- X return (-1);
- X sprintf(message, "number is required after -%c", c);
- X error(message);
- X exit(1);
- X }
- X
- X n = 0;
- X while (*s >= '0' && *s <= '9')
- X n = 10 * n + *s++ - '0';
- X *sp = s;
- X return (n);
- X}
- END_OF_FILE
- echo shar: Extracting \"prim.c\"
- sed "s/^X//" >'prim.c' <<'END_OF_FILE'
- X/*
- X * Primitives for displaying the file on the screen.
- X */
- X
- X#include "less.h"
- X#include "position.h"
- X
- Xpublic int hit_eof; /* Keeps track of how many times we hit end of file */
- Xpublic int screen_trashed;
- X
- Xstatic int squished;
- X
- Xextern int quiet;
- Xextern int sigs;
- Xextern int how_search;
- Xextern int top_scroll;
- Xextern int back_scroll;
- Xextern int sc_width, sc_height;
- Xextern int quit_at_eof;
- Xextern int caseless;
- Xextern int linenums;
- Xextern int plusoption;
- Xextern char *line;
- Xextern char *first_cmd;
- X#if TAGS
- Xextern int tagoption;
- X#endif
- X
- X/*
- X * Sound the bell to indicate he is trying to move past end of file.
- X */
- X static void
- Xeof_bell()
- X{
- X if (quiet == NOT_QUIET)
- X bell();
- X else
- X vbell();
- X}
- X
- X/*
- X * Check to see if the end of file is currently "displayed".
- X */
- X static void
- Xeof_check()
- X{
- X POSITION pos;
- X
- X if (sigs)
- X return;
- X /*
- X * If the bottom line is empty, we are at EOF.
- X * If the bottom line ends at the file length,
- X * we must be just at EOF.
- X */
- X pos = position(BOTTOM_PLUS_ONE);
- X if (pos == NULL_POSITION || pos == ch_length())
- X hit_eof++;
- X}
- X
- X/*
- X * If the screen is "squished", repaint it.
- X * "Squished" means the first displayed line is not at the top
- X * of the screen; this can happen when we display a short file
- X * for the first time.
- X */
- X static void
- Xsquish_check()
- X{
- X if (!squished)
- X return;
- X squished = 0;
- X repaint();
- X}
- X
- X/*
- X * Display n lines, scrolling forward,
- X * starting at position pos in the input file.
- X * "force" means display the n lines even if we hit end of file.
- X * "only_last" means display only the last screenful if n > screen size.
- X */
- X static void
- Xforw(n, pos, force, only_last)
- X register int n;
- X POSITION pos;
- X int force;
- X int only_last;
- X{
- X int eof = 0;
- X int nlines = 0;
- X int do_repaint;
- X static int first_time = 1;
- X
- X squish_check();
- X
- X /*
- X * do_repaint tells us not to display anything till the end,
- X * then just repaint the entire screen.
- X */
- X do_repaint = (only_last && n > sc_height-1);
- X
- X if (!do_repaint)
- X {
- X if (top_scroll && n >= sc_height - 1)
- X {
- X /*
- X * Start a new screen.
- X * {{ This is not really desirable if we happen
- X * to hit eof in the middle of this screen,
- X * but we don't yet know if that will happen. }}
- X */
- X if (top_scroll == 2)
- X clear();
- X home();
- X force = 1;
- X } else
- X {
- X lower_left();
- X clear_eol();
- X }
- X
- X if (pos != position(BOTTOM_PLUS_ONE))
- X {
- X /*
- X * This is not contiguous with what is
- X * currently displayed. Clear the screen image
- X * (position table) and start a new screen.
- X */
- X pos_clear();
- X add_forw_pos(pos);
- X force = 1;
- X if (top_scroll)
- X {
- X if (top_scroll == 2)
- X clear();
- X home();
- X } else if (!first_time)
- X {
- X putstr("...skipping...\n");
- X }
- X }
- X }
- X
- X while (--n >= 0)
- X {
- X /*
- X * Read the next line of input.
- X */
- X pos = forw_line(pos);
- X if (pos == NULL_POSITION)
- X {
- X /*
- X * End of file: stop here unless the top line
- X * is still empty, or "force" is true.
- X */
- X eof = 1;
- X if (!force && position(TOP) != NULL_POSITION)
- X break;
- X line = NULL;
- X }
- X /*
- X * Add the position of the next line to the position table.
- X * Display the current line on the screen.
- X */
- X add_forw_pos(pos);
- X nlines++;
- X if (do_repaint)
- X continue;
- X /*
- X * If this is the first screen displayed and
- X * we hit an early EOF (i.e. before the requested
- X * number of lines), we "squish" the display down
- X * at the bottom of the screen.
- X * But don't do this if a + option or a -t option
- X * was given. These options can cause us to
- X * start the display after the beginning of the file,
- X * and it is not appropriate to squish in that case.
- X */
- X if (first_time && line == NULL && !top_scroll &&
- X#if TAGS
- X !tagoption &&
- X#endif
- X !plusoption)
- X {
- X squished = 1;
- X continue;
- X }
- X if (top_scroll == 1)
- X clear_eol();
- X put_line();
- X }
- X
- X if (eof && !sigs)
- X hit_eof++;
- X else
- X eof_check();
- X if (nlines == 0)
- X eof_bell();
- X else if (do_repaint)
- X repaint();
- X first_time = 0;
- X (void) currline(BOTTOM);
- X}
- X
- X/*
- X * Display n lines, scrolling backward.
- X */
- X static void
- Xback(n, pos, force, only_last)
- X register int n;
- X POSITION pos;
- X int force;
- X int only_last;
- X{
- X int nlines = 0;
- X int do_repaint;
- X
- X squish_check();
- X do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
- X hit_eof = 0;
- X while (--n >= 0)
- X {
- X /*
- X * Get the previous line of input.
- X */
- X pos = back_line(pos);
- X if (pos == NULL_POSITION)
- X {
- X /*
- X * Beginning of file: stop here unless "force" is true.
- X */
- X if (!force)
- X break;
- X line = NULL;
- X }
- X /*
- X * Add the position of the previous line to the position table.
- X * Display the line on the screen.
- X */
- X add_back_pos(pos);
- X nlines++;
- X if (!do_repaint)
- X {
- X home();
- X add_line();
- X put_line();
- X }
- X }
- X
- X eof_check();
- X if (nlines == 0)
- X eof_bell();
- X else if (do_repaint)
- X repaint();
- X (void) currline(BOTTOM);
- X}
- X
- X/*
- X * Display n more lines, forward.
- X * Start just after the line currently displayed at the bottom of the screen.
- X */
- X public void
- Xforward(n, only_last)
- X int n;
- X int only_last;
- X{
- X POSITION pos;
- X
- X if (quit_at_eof && hit_eof)
- X {
- X /*
- X * If the -e flag is set and we're trying to go
- X * forward from end-of-file, go on to the next file.
- X */
- X next_file(1);
- X return;
- X }
- X
- X pos = position(BOTTOM_PLUS_ONE);
- X if (pos == NULL_POSITION)
- X {
- X eof_bell();
- X hit_eof++;
- X return;
- X }
- X forw(n, pos, 0, only_last);
- X}
- X
- X/*
- X * Display n more lines, backward.
- X * Start just before the line currently displayed at the top of the screen.
- X */
- X public void
- Xbackward(n, only_last)
- X int n;
- X int only_last;
- X{
- X POSITION pos;
- X
- X pos = position(TOP);
- X if (pos == NULL_POSITION)
- X {
- X /*
- X * This will almost never happen,
- X * because the top line is almost never empty.
- X */
- X eof_bell();
- X return;
- X }
- X back(n, pos, 0, only_last);
- X}
- X
- X/*
- X * Repaint the screen, starting from a specified position.
- X */
- X static void
- Xprepaint(pos)
- X POSITION pos;
- X{
- X hit_eof = 0;
- X forw(sc_height-1, pos, 1, 0);
- X screen_trashed = 0;
- X}
- X
- X/*
- X * Repaint the screen.
- X */
- X public void
- Xrepaint()
- X{
- X /*
- X * Start at the line currently at the top of the screen
- X * and redisplay the screen.
- X */
- X prepaint(position(TOP));
- X}
- X
- X/*
- X * Jump to the end of the file.
- X * It is more convenient to paint the screen backward,
- X * from the end of the file toward the beginning.
- X */
- X public void
- Xjump_forw()
- X{
- X POSITION pos;
- X
- X if (ch_end_seek())
- X {
- X error("Cannot seek to end of file");
- X return;
- X }
- X lastmark();
- X pos = ch_tell();
- X clear();
- X pos_clear();
- X add_back_pos(pos);
- X back(sc_height - 1, pos, 0, 0);
- X}
- X
- X/*
- X * Jump to line n in the file.
- X */
- X public void
- Xjump_back(n)
- X register int n;
- X{
- X register int c;
- X int nlines;
- X
- X /*
- X * This is done the slow way, by starting at the beginning
- X * of the file and counting newlines.
- X *
- X * {{ Now that we have line numbering (in linenum.c),
- X * we could improve on this by starting at the
- X * nearest known line rather than at the beginning. }}
- X */
- X if (ch_seek((POSITION)0))
- X {
- X /*
- X * Probably a pipe with beginning of file no longer buffered.
- X * If he wants to go to line 1, we do the best we can,
- X * by going to the first line which is still buffered.
- X */
- X if (n <= 1 && ch_beg_seek() == 0)
- X jump_loc(ch_tell());
- X error("Cannot get to beginning of file");
- X return;
- X }
- X
- X /*
- X * Start counting lines.
- X */
- X for (nlines = 1; nlines < n; nlines++)
- X {
- X while ((c = ch_forw_get()) != '\n')
- X if (c == EOI)
- X {
- X char message[40];
- X sprintf(message, "File has only %d lines",
- X nlines-1);
- X error(message);
- X return;
- X }
- X }
- X
- X jump_loc(ch_tell());
- X}
- X
- X/*
- X * Jump to a specified percentage into the file.
- X * This is a poor compensation for not being able to
- X * quickly jump to a specific line number.
- X */
- X public void
- Xjump_percent(percent)
- X int percent;
- X{
- X POSITION pos, len;
- X register int c;
- X
- X /*
- X * Determine the position in the file
- X * (the specified percentage of the file's length).
- X */
- X if ((len = ch_length()) == NULL_POSITION)
- X {
- X error("Don't know length of file");
- X return;
- X }
- X pos = (percent * len) / 100;
- X
- X /*
- X * Back up to the beginning of the line.
- X */
- X if (ch_seek(pos) == 0)
- X {
- X while ((c = ch_back_get()) != '\n' && c != EOI)
- X ;
- X if (c == '\n')
- X (void) ch_forw_get();
- X pos = ch_tell();
- X }
- X jump_loc(pos);
- X}
- X
- X/*
- X * Jump to a specified position in the file.
- X */
- X public void
- Xjump_loc(pos)
- X POSITION pos;
- X{
- X register int nline;
- X POSITION tpos;
- X
- X if ((nline = onscreen(pos)) >= 0)
- X {
- X /*
- X * The line is currently displayed.
- X * Just scroll there.
- X */
- X forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
- X return;
- X }
- X
- X /*
- X * Line is not on screen.
- X * Seek to the desired location.
- X */
- X if (ch_seek(pos))
- X {
- X error("Cannot seek to that position");
- X return;
- X }
- X
- X /*
- X * See if the desired line is BEFORE the currently
- X * displayed screen. If so, then move forward far
- X * enough so the line we're on will be at the bottom
- X * of the screen, in order to be able to call back()
- X * to make the screen scroll backwards & put the line
- X * at the top of the screen.
- X * {{ This seems inefficient, but it's not so bad,
- X * since we can never move forward more than a
- X * screenful before we stop to redraw the screen. }}
- X */
- X tpos = position(TOP);
- X if (tpos != NULL_POSITION && pos < tpos)
- X {
- X POSITION npos = pos;
- X /*
- X * Note that we can't forw_line() past tpos here,
- X * so there should be no EOI at this stage.
- X */
- X for (nline = 0; npos < tpos && nline < sc_height - 1; nline++)
- X npos = forw_line(npos);
- X
- X if (npos < tpos)
- X {
- X /*
- X * More than a screenful back.
- X */
- X lastmark();
- X clear();
- X pos_clear();
- X add_back_pos(npos);
- X }
- X
- X /*
- X * Note that back() will repaint() if nline > back_scroll.
- X */
- X back(nline, npos, 1, 0);
- X return;
- X }
- X /*
- X * Remember where we were; clear and paint the screen.
- X */
- X lastmark();
- X prepaint(pos);
- X}
- X
- X/*
- X * The table of marks.
- X * A mark is simply a position in the file.
- X */
- X#define NMARKS (27) /* 26 for a-z plus one for quote */
- X#define LASTMARK (NMARKS-1) /* For quote */
- Xstatic POSITION marks[NMARKS];
- X
- X/*
- X * Initialize the mark table to show no marks are set.
- X */
- X public void
- Xinit_mark()
- X{
- X int i;
- X
- X for (i = 0; i < NMARKS; i++)
- X marks[i] = NULL_POSITION;
- X}
- X
- X/*
- X * See if a mark letter is valid (between a and z).
- X */
- X static int
- Xbadmark(c)
- X int c;
- X{
- X if (c < 'a' || c > 'z')
- X {
- X error("Choose a letter between 'a' and 'z'");
- X return (1);
- X }
- X return (0);
- X}
- X
- X/*
- X * Set a mark.
- X */
- X public void
- Xsetmark(c)
- X int c;
- X{
- X if (badmark(c))
- X return;
- X marks[c-'a'] = position(TOP);
- X}
- X
- X public void
- Xlastmark()
- X{
- X marks[LASTMARK] = position(TOP);
- X}
- X
- X/*
- X * Go to a previously set mark.
- X */
- X public void
- Xgomark(c)
- X int c;
- X{
- X POSITION pos;
- X
- X if (c == '\'')
- X pos = marks[LASTMARK];
- X else if (badmark(c))
- X return;
- X else
- X pos = marks[c-'a'];
- X
- X if (pos == NULL_POSITION)
- X error("mark not set");
- X else
- X jump_loc(pos);
- X}
- X
- X/*
- X * Get the backwards scroll limit.
- X * Must call this function instead of just using the value of
- X * back_scroll, because the default case depends on sc_height and
- X * top_scroll, as well as back_scroll.
- X */
- X public int
- Xget_back_scroll()
- X{
- X if (back_scroll >= 0)
- X return (back_scroll);
- X if (top_scroll)
- X return (sc_height - 2);
- X return (sc_height - 1);
- X}
- X
- X/*
- X * Search for the n-th occurence of a specified pattern,
- X * either forward or backward.
- X */
- X public void
- Xsearch(search_forward, pattern, n, wantmatch)
- X register int search_forward;
- X register char *pattern;
- X register int n;
- X int wantmatch;
- X{
- X POSITION pos, linepos;
- X register char *p;
- X register char *q;
- X int linenum;
- X int linematch;
- X#if RECOMP
- X char *re_comp();
- X char *errmsg;
- X#else
- X#if REGCMP
- X char *regcmp();
- X static char *cpattern = NULL;
- X#else
- X static char lpbuf[100];
- X static char *last_pattern = NULL;
- X#endif
- X#endif
- X
- X if (caseless && pattern != NULL)
- X {
- X /*
- X * For a caseless search, convert any uppercase
- X * in the pattern to lowercase.
- X */
- X for (p = pattern; *p != '\0'; p++)
- X if (*p >= 'A' && *p <= 'Z')
- X *p += 'a' - 'A';
- X }
- X#if RECOMP
- X
- X /*
- X * (re_comp handles a null pattern internally,
- X * so there is no need to check for a null pattern here.)
- X */
- X if ((errmsg = re_comp(pattern)) != NULL)
- X {
- X error(errmsg);
- X return;
- X }
- X#else
- X#if REGCMP
- X if (pattern == NULL || *pattern == '\0')
- X {
- X /*
- X * A null pattern means use the previous pattern.
- X * The compiled previous pattern is in cpattern, so just use it.
- X */
- X if (cpattern == NULL)
- X {
- X error("No previous regular expression");
- X return;
- X }
- X } else
- X {
- X /*
- X * Otherwise compile the given pattern.
- X */
- X char *s;
- X if ((s = regcmp(pattern, 0)) == NULL)
- X {
- X error("Invalid pattern");
- X return;
- X }
- X if (cpattern != NULL)
- X free(cpattern);
- X cpattern = s;
- X }
- X#else
- X if (pattern == NULL || *pattern == '\0')
- X {
- X /*
- X * Null pattern means use the previous pattern.
- X */
- X if (last_pattern == NULL)
- X {
- X error("No previous regular expression");
- X return;
- X }
- X pattern = last_pattern;
- X } else
- X {
- X strcpy(lpbuf, pattern);
- X last_pattern = lpbuf;
- X }
- X#endif
- X#endif
- X
- X /*
- X * Figure out where to start the search.
- X */
- X
- X if (position(TOP) == NULL_POSITION)
- X {
- X /*
- X * Nothing is currently displayed.
- X * Start at the beginning of the file.
- X * (This case is mainly for first_cmd searches,
- X * for example, "+/xyz" on the command line.)
- X */
- X pos = (POSITION)0;
- X } else if (!search_forward)
- X {
- X /*
- X * Backward search: start just before the top line
- X * displayed on the screen.
- X */
- X pos = position(TOP);
- X } else if (how_search == 0)
- X {
- X /*
- X * Start at the second real line displayed on the screen.
- X */
- X pos = position(TOP);
- X do
- X pos = forw_raw_line(pos);
- X while (pos < position(TOP+1));
- X } else if (how_search == 1)
- X {
- X /*
- X * Start just after the bottom line displayed on the screen.
- X */
- X pos = position(BOTTOM_PLUS_ONE);
- X } else
- X {
- X /*
- X * Start at the second screen line displayed on the screen.
- X */
- X pos = position(TOP_PLUS_ONE);
- X }
- X
- X if (pos == NULL_POSITION)
- X {
- X /*
- X * Can't find anyplace to start searching from.
- X */
- X error("Nothing to search");
- X return;
- X }
- X
- X linenum = find_linenum(pos);
- X for (;;)
- X {
- X /*
- X * Get lines until we find a matching one or
- X * until we hit end-of-file (or beginning-of-file
- X * if we're going backwards).
- X */
- X if (sigs)
- X /*
- X * A signal aborts the search.
- X */
- X return;
- X
- X if (search_forward)
- X {
- X /*
- X * Read the next line, and save the
- X * starting position of that line in linepos.
- X */
- X linepos = pos;
- X pos = forw_raw_line(pos);
- X if (linenum != 0)
- X linenum++;
- X } else
- X {
- X /*
- X * Read the previous line and save the
- X * starting position of that line in linepos.
- X */
- X pos = back_raw_line(pos);
- X linepos = pos;
- X if (linenum != 0)
- X linenum--;
- X }
- X
- X if (pos == NULL_POSITION)
- X {
- X /*
- X * We hit EOF/BOF without a match.
- X */
- X error("Pattern not found");
- X return;
- X }
- X
- X /*
- X * If we're using line numbers, we might as well
- X * remember the information we have now (the position
- X * and line number of the current line).
- X */
- X if (linenums)
- X add_lnum(linenum, pos);
- X
- X if (caseless)
- X {
- X /*
- X * If this is a caseless search, convert
- X * uppercase in the input line to lowercase.
- X * While we're at it, remove any backspaces
- X * along with the preceeding char.
- X * This allows us to match text which is
- X * underlined or overstruck.
- X */
- X for (p = q = line; *p != '\0'; p++, q++)
- X {
- X if (*p >= 'A' && *p <= 'Z')
- X /* Convert uppercase to lowercase. */
- X *q = *p + 'a' - 'A';
- X else if (q > line && *p == '\b')
- X /* Delete BS and preceeding char. */
- X q -= 2;
- X else
- X /* Otherwise, just copy. */
- X *q = *p;
- X }
- X }
- X
- X /*
- X * Test the next line to see if we have a match.
- X * This is done in a variety of ways, depending
- X * on what pattern matching functions are available.
- X */
- X#if REGCMP
- X linematch = (regex(cpattern, line) != NULL);
- X#else
- X#if RECOMP
- X linematch = (re_exec(line) == 1);
- X#else
- X linematch = match(pattern, line);
- X#endif
- X#endif
- X /*
- X * We are successful if wantmatch and linematch are
- X * both true (want a match and got it),
- X * or both false (want a non-match and got it).
- X */
- X if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
- X --n <= 0)
- X /*
- X * Found the line.
- X */
- X break;
- X }
- X
- X jump_loc(linepos);
- X}
- X
- X#if (!REGCMP) && (!RECOMP)
- X/*
- X * We have neither regcmp() nor re_comp().
- X * We use this function to do simple pattern matching.
- X * It supports no metacharacters like *, etc.
- X */
- X static int
- Xmatch(pattern, buf)
- X char *pattern, *buf;
- X{
- X register char *pp, *lp;
- X
- X for ( ; *buf != '\0'; buf++)
- X {
- X for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
- X if (*pp == '\0' || *lp == '\0')
- X break;
- X if (*pp == '\0')
- X return (1);
- X }
- X return (0);
- X}
- X#endif
- END_OF_FILE
- echo shar: Extracting \"ch.c\"
- sed "s/^X//" >'ch.c' <<'END_OF_FILE'
- X/*
- X * Low level character input from the input file.
- X * We use these special purpose routines which optimize moving
- X * both forward and backward from the current read pointer.
- X */
- X
- X#include "less.h"
- X
- Xpublic int file = -1; /* File descriptor of the input file */
- X
- X/*
- X * Pool of buffers holding the most recently used blocks of the input file.
- X */
- X#define BUFSIZ 1024
- Xstruct buf {
- X struct buf *next, *prev;
- X long block;
- X int datasize;
- X char data[BUFSIZ];
- X};
- Xpublic int nbufs;
- X
- X/*
- X * The buffer pool is kept as a doubly-linked circular list,
- X * in order from most- to least-recently used.
- X * The circular list is anchored by buf_anchor.
- X */
- X#define END_OF_CHAIN ((struct buf *)&buf_anchor)
- X#define buf_head buf_anchor.next
- X#define buf_tail buf_anchor.prev
- X
- Xstatic struct {
- X struct buf *next, *prev;
- X} buf_anchor = { END_OF_CHAIN, END_OF_CHAIN };
- X
- Xextern int clean_data;
- Xextern int ispipe;
- Xextern int autobuf;
- Xextern int cbufs;
- Xextern int sigs;
- X#if LOGFILE
- Xextern int logfile;
- X#endif
- X
- X/*
- X * Current position in file.
- X * Stored as a block number and an offset into the block.
- X */
- Xstatic long ch_block;
- Xstatic int ch_offset;
- X
- X/*
- X * Length of file, needed if input is a pipe.
- X */
- Xstatic POSITION ch_fsize;
- X
- X/*
- X * Number of bytes read, if input is standard input (a pipe).
- X */
- Xstatic POSITION last_piped_pos;
- X
- X/*
- X * Get the character pointed to by the read pointer.
- X * ch_get() is a macro which is more efficient to call
- X * than fch_get (the function), in the usual case
- X * that the block desired is at the head of the chain.
- X */
- X#define ch_get() ((buf_head->block == ch_block && \
- X ch_offset < buf_head->datasize) ? \
- X buf_head->data[ch_offset] : fch_get())
- X static int
- Xfch_get()
- X{
- X register struct buf *bp;
- X register int n;
- X register char *p;
- X POSITION pos;
- X
- X /*
- X * Look for a buffer holding the desired block.
- X */
- X for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
- X if (bp->block == ch_block)
- X {
- X if (ch_offset >= bp->datasize)
- X /*
- X * Need more data in this buffer.
- X */
- X goto read_more;
- X /*
- X * On a pipe, we don't sort the buffers LRU
- X * because this can cause gaps in the buffers.
- X * For example, suppose we've got 12 1K buffers,
- X * and a 15K input stream. If we read the first 12K
- X * sequentially, then jump to line 1, then jump to
- X * the end, the buffers have blocks 0,4,5,6,..,14.
- X * If we then jump to line 1 again and try to
- X * read sequentially, we're out of luck when we
- X * get to block 1 (we'd get the "pipe error" below).
- X * To avoid this, we only sort buffers on a pipe
- X * when we actually READ the data, not when we
- X * find it already buffered.
- X */
- X if (ispipe)
- X return (bp->data[ch_offset]);
- X goto found;
- X }
- X /*
- X * Block is not in a buffer.
- X * Take the least recently used buffer
- X * and read the desired block into it.
- X * If the LRU buffer has data in it,
- X * and autobuf is true, and input is a pipe,
- X * then try to allocate a new buffer first.
- X */
- X if (autobuf && ispipe && buf_tail->block != (long)(-1))
- X (void) ch_addbuf(1);
- X bp = buf_tail;
- X bp->block = ch_block;
- X bp->datasize = 0;
- X
- X read_more:
- X pos = (ch_block * BUFSIZ) + bp->datasize;
- X if (ispipe)
- X {
- X /*
- X * The data requested should be immediately after
- X * the last data read from the pipe.
- X */
- X if (pos != last_piped_pos)
- X {
- X error("pipe error");
- X quit();
- X }
- X } else
- X lseek(file, pos, 0);
- X
- X /*
- X * Read the block.
- X * If we read less than a full block, we just return the
- X * partial block and pick up the rest next time.
- X */
- X n = iread(file, &bp->data[bp->datasize], BUFSIZ - bp->datasize);
- X if (n == READ_INTR)
- X return (EOI);
- X if (n < 0)
- X {
- X error("read error");
- X quit();
- X }
- X if (ispipe)
- X last_piped_pos += n;
- X
- X#if LOGFILE
- X /*
- X * If we have a log file, write the new data to it.
- X */
- X if (logfile >= 0 && n > 0)
- X write(logfile, &bp->data[bp->datasize], n);
- X#endif
- X
- X bp->datasize += n;
- X
- X /*
- X * Set an EOI marker in the buffered data itself.
- X * Then ensure the data is "clean": there are no
- X * extra EOI chars in the data and that the "meta"
- X * bit (the 0200 bit) is reset in each char.
- X */
- X if (n == 0)
- X {
- X ch_fsize = pos;
- X bp->data[bp->datasize++] = EOI;
- X }
- X
- X if (!clean_data)
- X {
- X p = &bp->data[bp->datasize];
- X while (--n >= 0)
- X {
- X *--p &= 0177;
- X if (*p == EOI)
- X *p = '@';
- X }
- X }
- X
- X found:
- X if (buf_head != bp)
- X {
- X /*
- X * Move the buffer to the head of the buffer chain.
- X * This orders the buffer chain, most- to least-recently used.
- X */
- X bp->next->prev = bp->prev;
- X bp->prev->next = bp->next;
- X
- X bp->next = buf_head;
- X bp->prev = END_OF_CHAIN;
- X buf_head->prev = bp;
- X buf_head = bp;
- X }
- X
- X if (ch_offset >= bp->datasize)
- X /*
- X * After all that, we still don't have enough data.
- X * Go back and try again.
- X */
- X goto read_more;
- X
- X return (bp->data[ch_offset]);
- X}
- X
- X#if LOGFILE
- X/*
- X * Close the logfile.
- X * If we haven't read all of standard input into it, do that now.
- X */
- X public void
- Xend_logfile()
- X{
- X static int tried = 0;
- X
- X if (logfile < 0)
- X return;
- X if (!tried && ch_fsize == NULL_POSITION)
- X {
- X tried = 1;
- X ierror("finishing logfile");
- X while (ch_forw_get() != EOI)
- X if (sigs)
- X break;
- X }
- X close(logfile);
- X logfile = -1;
- X}
- X
- X/*
- X * Start a log file AFTER less has already been running.
- X * Invoked from the - command; see toggle_option().
- X * Write all the existing buffered data to the log file.
- X */
- X public void
- Xsync_logfile()
- X{
- X register struct buf *bp;
- X register int n;
- X long block;
- X long last_block;
- X
- X last_block = (last_piped_pos + BUFSIZ - 1) / BUFSIZ;
- X for (block = 0; block <= last_block; block++)
- X for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
- X if (bp->block == block)
- X {
- X n = bp->datasize;
- X if (bp->data[n-1] == EOI)
- X n--;
- X write(logfile, bp->data, n);
- X break;
- X }
- X}
- X
- X#endif
- X
- X/*
- X * Determine if a specific block is currently in one of the buffers.
- X */
- X static int
- Xbuffered(block)
- X long block;
- X{
- X register struct buf *bp;
- X
- X for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
- X if (bp->block == block)
- X return (1);
- X return (0);
- X}
- X
- X/*
- X * Seek to a specified position in the file.
- X * Return 0 if successful, non-zero if can't seek there.
- X */
- X public int
- Xch_seek(pos)
- X register POSITION pos;
- X{
- X long new_block;
- X
- X new_block = pos / BUFSIZ;
- X if (!ispipe || pos == last_piped_pos || buffered(new_block))
- X {
- X /*
- X * Set read pointer.
- X */
- X ch_block = new_block;
- X ch_offset = pos % BUFSIZ;
- X return (0);
- X }
- X return (1);
- X}
- X
- X/*
- X * Seek to the end of the file.
- X */
- X public int
- Xch_end_seek()
- X{
- X if (!ispipe)
- X return (ch_seek(ch_length()));
- X
- X /*
- X * Do it the slow way: read till end of data.
- X */
- X while (ch_forw_get() != EOI)
- X if (sigs)
- X return (1);
- X return (0);
- X}
- X
- X/*
- X * Seek to the beginning of the file, or as close to it as we can get.
- X * We may not be able to seek there if input is a pipe and the
- X * beginning of the pipe is no longer buffered.
- X */
- X public int
- Xch_beg_seek()
- X{
- X register struct buf *bp, *firstbp;
- X
- X /*
- X * Try a plain ch_seek first.
- X */
- X if (ch_seek((POSITION)0) == 0)
- X return (0);
- X
- X /*
- X * Can't get to position 0.
- X * Look thru the buffers for the one closest to position 0.
- X */
- X firstbp = bp = buf_head;
- X if (bp == END_OF_CHAIN)
- X return (1);
- X while ((bp = bp->next) != END_OF_CHAIN)
- X if (bp->block < firstbp->block)
- X firstbp = bp;
- X ch_block = firstbp->block;
- X ch_offset = 0;
- X return (0);
- X}
- X
- X/*
- X * Return the length of the file, if known.
- X */
- X public POSITION
- Xch_length()
- X{
- X if (ispipe)
- X return (ch_fsize);
- X return ((POSITION)(lseek(file, (offset_t)0, 2)));
- X}
- X
- X/*
- X * Return the current position in the file.
- X */
- X public POSITION
- Xch_tell()
- X{
- X return (ch_block * BUFSIZ + ch_offset);
- X}
- X
- X/*
- X * Get the current char and post-increment the read pointer.
- X */
- X public int
- Xch_forw_get()
- X{
- X register int c;
- X
- X c = ch_get();
- X if (c != EOI && ++ch_offset >= BUFSIZ)
- X {
- X ch_offset = 0;
- X ch_block ++;
- X }
- X return (c);
- X}
- X
- X/*
- X * Pre-decrement the read pointer and get the new current char.
- X */
- X public int
- Xch_back_get()
- X{
- X if (--ch_offset < 0)
- X {
- X if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
- X {
- X ch_offset = 0;
- X return (EOI);
- X }
- X ch_offset = BUFSIZ - 1;
- X ch_block--;
- X }
- X return (ch_get());
- X}
- X
- X/*
- X * Allocate buffers.
- X * Caller wants us to have a total of at least want_nbufs buffers.
- X * keep==1 means keep the data in the current buffers;
- X * otherwise discard the old data.
- X */
- X public void
- Xch_init(want_nbufs, keep)
- X int want_nbufs;
- X int keep;
- X{
- X register struct buf *bp;
- X char message[80];
- X
- X cbufs = nbufs;
- X if (nbufs < want_nbufs && ch_addbuf(want_nbufs - nbufs))
- X {
- X /*
- X * Cannot allocate enough buffers.
- X * If we don't have ANY, then quit.
- X * Otherwise, just report the error and return.
- X */
- X sprintf(message, "cannot allocate %d buffers",
- X want_nbufs - nbufs);
- X error(message);
- X if (nbufs == 0)
- X quit();
- X return;
- X }
- X
- X if (keep)
- X return;
- X
- X /*
- X * We don't want to keep the old data,
- X * so initialize all the buffers now.
- X */
- X for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
- X bp->block = (long)(-1);
- X last_piped_pos = (POSITION)0;
- X ch_fsize = NULL_POSITION;
- X (void) ch_seek((POSITION)0);
- X}
- X
- X/*
- X * Allocate some new buffers.
- X * The buffers are added to the tail of the buffer chain.
- X */
- X static int
- Xch_addbuf(nnew)
- X int nnew;
- X{
- X register struct buf *bp;
- X register struct buf *newbufs;
- X
- X /*
- X * We don't have enough buffers.
- X * Allocate some new ones.
- X */
- X newbufs = (struct buf *) calloc(nnew, sizeof(struct buf));
- X if (newbufs == NULL)
- X return (1);
- X
- X /*
- X * Initialize the new buffers and link them together.
- X * Link them all onto the tail of the buffer list.
- X */
- X nbufs += nnew;
- X cbufs = nbufs;
- X for (bp = &newbufs[0]; bp < &newbufs[nnew]; bp++)
- X {
- X bp->next = bp + 1;
- X bp->prev = bp - 1;
- X bp->block = (long)(-1);
- X }
- X newbufs[nnew-1].next = END_OF_CHAIN;
- X newbufs[0].prev = buf_tail;
- X buf_tail->next = &newbufs[0];
- X buf_tail = &newbufs[nnew-1];
- X return (0);
- X}
- END_OF_FILE
- echo shar: Extracting \"position.c\"
- sed "s/^X//" >'position.c' <<'END_OF_FILE'
- X/*
- X * Routines dealing with the "position" table.
- X * This is a table which tells the position (in the input file) of the
- X * first char on each currently displayed line.
- X *
- X * {{ The position table is scrolled by moving all the entries.
- X * Would be better to have a circular table
- X * and just change a couple of pointers. }}
- X */
- X
- X#include "less.h"
- X#include "position.h"
- X
- X#define NPOS 100 /* {{ sc_height must be less than NPOS }} */
- Xstatic POSITION table[NPOS]; /* The position table */
- X
- Xextern int sc_width, sc_height;
- X
- X/*
- X * Return the starting file position of a line displayed on the screen.
- X * The line may be specified as a line number relative to the top
- X * of the screen, but is usually one of these special cases:
- X * the top (first) line on the screen
- X * the second line on the screen
- X * the bottom line on the screen
- X * the line after the bottom line on the screen
- X */
- X public POSITION
- Xposition(where)
- X int where;
- X{
- X switch (where)
- X {
- X case BOTTOM:
- X where = sc_height - 2;
- X break;
- X case BOTTOM_PLUS_ONE:
- X where = sc_height - 1;
- X break;
- X case MIDDLE:
- X where = sc_height / 2;
- X }
- X return (table[where]);
- X}
- X
- X/*
- X * Add a new file position to the bottom of the position table.
- X */
- X public void
- Xadd_forw_pos(pos)
- X POSITION pos;
- X{
- X register int i;
- X
- X /*
- X * Scroll the position table up.
- X */
- X for (i = 1; i < sc_height; i++)
- X table[i-1] = table[i];
- X table[sc_height - 1] = pos;
- X}
- X
- X/*
- X * Add a new file position to the top of the position table.
- X */
- X public void
- Xadd_back_pos(pos)
- X POSITION pos;
- X{
- X register int i;
- X
- X /*
- X * Scroll the position table down.
- X */
- X for (i = sc_height - 1; i > 0; i--)
- X table[i] = table[i-1];
- X table[0] = pos;
- X}
- X
- X/*
- X * Initialize the position table, done whenever we clear the screen.
- X */
- X public void
- Xpos_clear()
- X{
- X register int i;
- X
- X for (i = 0; i < sc_height; i++)
- X table[i] = NULL_POSITION;
- X}
- X
- X/*
- X * See if the byte at a specified position is currently on the screen.
- X * Check the position table to see if the position falls within its range.
- X * Return the position table entry if found, -1 if not.
- X */
- X public int
- Xonscreen(pos)
- X POSITION pos;
- X{
- X register int i;
- X
- X if (pos < table[0])
- X return (-1);
- X for (i = 1; i < sc_height; i++)
- X if (pos < table[i])
- X return (i-1);
- X return (-1);
- X}
- END_OF_FILE
- echo shar: Extracting \"input.c\"
- sed "s/^X//" >'input.c' <<'END_OF_FILE'
- X/*
- X * High level routines dealing with getting lines of input
- X * from the file being viewed.
- X *
- X * When we speak of "lines" here, we mean PRINTABLE lines;
- X * lines processed with respect to the screen width.
- X * We use the term "raw line" to refer to lines simply
- X * delimited by newlines; not processed with respect to screen width.
- X */
- X
- X#include "less.h"
- X
- Xextern int squeeze;
- Xextern int sigs;
- Xextern char *line;
- X
- X/*
- X * Get the next line.
- X * A "current" position is passed and a "new" position is returned.
- X * The current position is the position of the first character of
- X * a line. The new position is the position of the first character
- X * of the NEXT line. The line obtained is the line starting at curr_pos.
- X */
- X public POSITION
- Xforw_line(curr_pos)
- X POSITION curr_pos;
- X{
- X POSITION new_pos;
- X register int c;
- X
- X if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
- X return (NULL_POSITION);
- X
- X c = ch_forw_get();
- X if (c == EOI)
- X return (NULL_POSITION);
- X
- X prewind();
- X for (;;)
- X {
- X if (sigs)
- X return (NULL_POSITION);
- X if (c == '\n' || c == EOI)
- X {
- X /*
- X * End of the line.
- X */
- X new_pos = ch_tell();
- X break;
- X }
- X
- X /*
- X * Append the char to the line and get the next char.
- X */
- X if (pappend(c))
- X {
- X /*
- X * The char won't fit in the line; the line
- X * is too long to print in the screen width.
- X * End the line here.
- X */
- X new_pos = ch_tell() - 1;
- X break;
- X }
- X c = ch_forw_get();
- X }
- X (void) pappend('\0');
- X
- X if (squeeze && *line == '\0')
- X {
- X /*
- X * This line is blank.
- X * Skip down to the last contiguous blank line
- X * and pretend it is the one which we are returning.
- X */
- X while ((c = ch_forw_get()) == '\n')
- X if (sigs)
- X return (NULL_POSITION);
- X if (c != EOI)
- X (void) ch_back_get();
- X new_pos = ch_tell();
- X }
- X
- X return (new_pos);
- X}
- X
- X/*
- X * Get the previous line.
- X * A "current" position is passed and a "new" position is returned.
- X * The current position is the position of the first character of
- X * a line. The new position is the position of the first character
- X * of the PREVIOUS line. The line obtained is the one starting at new_pos.
- X */
- X public POSITION
- Xback_line(curr_pos)
- X POSITION curr_pos;
- X{
- X POSITION new_pos, begin_new_pos;
- X int c;
- X
- X if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
- X ch_seek(curr_pos-1))
- X return (NULL_POSITION);
- X
- X if (squeeze)
- X {
- X /*
- X * Find out if the "current" line was blank.
- X */
- X (void) ch_forw_get(); /* Skip the newline */
- X c = ch_forw_get(); /* First char of "current" line */
- X (void) ch_back_get(); /* Restore our position */
- X (void) ch_back_get();
- X
- X if (c == '\n')
- X {
- X /*
- X * The "current" line was blank.
- X * Skip over any preceeding blank lines,
- X * since we skipped them in forw_line().
- X */
- X while ((c = ch_back_get()) == '\n')
- X if (sigs)
- X return (NULL_POSITION);
- X if (c == EOI)
- X return (NULL_POSITION);
- X (void) ch_forw_get();
- X }
- X }
- X
- X /*
- X * Scan backwards until we hit the beginning of the line.
- X */
- X for (;;)
- X {
- X if (sigs)
- X return (NULL_POSITION);
- X c = ch_back_get();
- X if (c == '\n')
- X {
- X /*
- X * This is the newline ending the previous line.
- X * We have hit the beginning of the line.
- X */
- X new_pos = ch_tell() + 1;
- X break;
- X }
- X if (c == EOI)
- X {
- X /*
- X * We have hit the beginning of the file.
- X * This must be the first line in the file.
- X * This must, of course, be the beginning of the line.
- X */
- X new_pos = ch_tell();
- X break;
- X }
- X }
- X
- X /*
- X * Now scan forwards from the beginning of this line.
- X * We keep discarding "printable lines" (based on screen width)
- X * until we reach the curr_pos.
- X *
- X * {{ This algorithm is pretty inefficient if the lines
- X * are much longer than the screen width,
- X * but I don't know of any better way. }}
- X */
- X if (ch_seek(new_pos))
- X return (NULL_POSITION);
- X loop:
- X begin_new_pos = new_pos;
- X prewind();
- X
- X do
- X {
- X c = ch_forw_get();
- X if (c == EOI || sigs)
- X return (NULL_POSITION);
- X new_pos++;
- X if (c == '\n')
- X break;
- X if (pappend(c))
- X {
- X /*
- X * Got a full printable line, but we haven't
- X * reached our curr_pos yet. Discard the line
- X * and start a new one.
- X */
- X (void) pappend('\0');
- X (void) ch_back_get();
- X new_pos--;
- X goto loop;
- X }
- X } while (new_pos < curr_pos);
- X
- X (void) pappend('\0');
- X
- X return (begin_new_pos);
- X}
- END_OF_FILE
- echo shar: Extracting \"linenum.c\"
- sed "s/^X//" >'linenum.c' <<'END_OF_FILE'
- X/*
- X * Code to handle displaying line numbers.
- X *
- X * Finding the line number of a given file position is rather tricky.
- X * We don't want to just start at the beginning of the file and
- X * count newlines, because that is slow for large files (and also
- X * wouldn't work if we couldn't get to the start of the file; e.g.
- X * if input is a long pipe).
- X *
- X * So we use the function add_lnum to cache line numbers.
- X * We try to be very clever and keep only the more interesting
- X * line numbers when we run out of space in our table. A line
- X * number is more interesting than another when it is far from
- X * other line numbers. For example, we'd rather keep lines
- X * 100,200,300 than 100,101,300. 200 is more interesting than
- X * 101 because 101 can be derived very cheaply from 100, while
- X * 200 is more expensive to derive from 100.
- X *
- X * The function currline() returns the line number of a given
- X * position in the file. As a side effect, it calls add_lnum
- X * to cache the line number. Therefore currline is occasionally
- X * called to make sure we cache line numbers often enough.
- X */
- X
- X#include "less.h"
- X#include "position.h"
- X
- X/*
- X * Structure to keep track of a line number and the associated file position.
- X * A doubly-linked circular list of line numbers is kept ordered by line number.
- X */
- Xstruct linenum
- X{
- X struct linenum *next; /* Link to next in the list */
- X struct linenum *prev; /* Line to previous in the list */
- X POSITION pos; /* File position */
- X POSITION gap; /* Gap between prev and next */
- X int line; /* Line number */
- X};
- X/*
- X * "gap" needs some explanation: the gap of any particular line number
- X * is the distance between the previous one and the next one in the list.
- X * ("Distance" means difference in file position.) In other words, the
- X * gap of a line number is the gap which would be introduced if this
- X * line number were deleted. It is used to decide which one to replace
- X * when we have a new one to insert and the table is full.
- X */
- X
- X#define NPOOL 50 /* Size of line number pool */
- X
- X#define LONGTIME (2) /* In seconds */
- X
- Xpublic int lnloop = 0; /* Are we in the line num loop? */
- X
- Xstatic struct linenum anchor; /* Anchor of the list */
- Xstatic struct linenum *freelist; /* Anchor of the unused entries */
- Xstatic struct linenum pool[NPOOL]; /* The pool itself */
- Xstatic struct linenum *spare; /* We always keep one spare entry */
- X
- Xextern int linenums;
- Xextern int sigs;
- X
- X/*
- X * Initialize the line number structures.
- X */
- X public void
- Xclr_linenum()
- X{
- X register struct linenum *p;
- X
- X /*
- X * Put all the entries on the free list.
- X * Leave one for the "spare".
- X */
- X for (p = pool; p < &pool[NPOOL-2]; p++)
- X p->next = p+1;
- X pool[NPOOL-2].next = NULL;
- X freelist = pool;
- X
- X spare = &pool[NPOOL-1];
- X
- X /*
- X * Initialize the anchor.
- X */
- X anchor.next = anchor.prev = &anchor;
- X anchor.gap = 0;
- X anchor.pos = (POSITION)0;
- X anchor.line = 1;
- X}
- X
- X/*
- X * Calculate the gap for an entry.
- X */
- X static void
- Xcalcgap(p)
- X register struct linenum *p;
- X{
- X /*
- X * Don't bother to compute a gap for the anchor.
- X * Also don't compute a gap for the last one in the list.
- X * The gap for that last one should be considered infinite,
- X * but we never look at it anyway.
- X */
- X if (p == &anchor || p->next == &anchor)
- X return;
- X p->gap = p->next->pos - p->prev->pos;
- X}
- X
- X/*
- X * Add a new line number to the cache.
- X * The specified position (pos) should be the file position of the
- X * FIRST character in the specified line.
- X */
- X public void
- Xadd_lnum(line, pos)
- X int line;
- X POSITION pos;
- X{
- X register struct linenum *p;
- X register struct linenum *new;
- X register struct linenum *nextp;
- X register struct linenum *prevp;
- X register POSITION mingap;
- X
- X /*
- X * Find the proper place in the list for the new one.
- X * The entries are sorted by position.
- X */
- X for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
- X if (p->line == line)
- X /* We already have this one. */
- X return;
- X nextp = p;
- X prevp = p->prev;
- X
- X if (freelist != NULL)
- X {
- X /*
- X * We still have free (unused) entries.
- X * Use one of them.
- X */
- X new = freelist;
- X freelist = freelist->next;
- X } else
- X {
- X /*
- X * No free entries.
- X * Use the "spare" entry.
- X */
- X new = spare;
- X spare = NULL;
- X }
- X
- X /*
- X * Fill in the fields of the new entry,
- X * and insert it into the proper place in the list.
- X */
- X new->next = nextp;
- X new->prev = prevp;
- X new->pos = pos;
- X new->line = line;
- X
- X nextp->prev = new;
- X prevp->next = new;
- X
- X /*
- X * Recalculate gaps for the new entry and the neighboring entries.
- X */
- X calcgap(new);
- X calcgap(nextp);
- X calcgap(prevp);
- X
- X if (spare == NULL)
- X {
- X /*
- X * We have used the spare entry.
- X * Scan the list to find the one with the smallest
- X * gap, take it out and make it the spare.
- X * We should never remove the last one, so stop when
- X * we get to p->next == &anchor. This also avoids
- X * looking at the gap of the last one, which is
- X * not computed by calcgap.
- X */
- X mingap = anchor.next->gap;
- X for (p = anchor.next; p->next != &anchor; p = p->next)
- X {
- X if (p->gap <= mingap)
- X {
- X spare = p;
- X mingap = p->gap;
- X }
- X }
- X spare->next->prev = spare->prev;
- X spare->prev->next = spare->next;
- X }
- X}
- X
- X/*
- X * If we get stuck in a long loop trying to figure out the
- X * line number, print a message to tell the user what we're doing.
- X */
- X static void
- Xlongloopmessage()
- X{
- X ierror("Calculating line numbers");
- X /*
- X * Set the lnloop flag here, so if the user interrupts while
- X * we are calculating line numbers, the signal handler will
- X * turn off line numbers (linenums=0).
- X */
- X lnloop = 1;
- X}
- X
- X/*
- X * Find the line number associated with a given position.
- X * Return 0 if we can't figure it out.
- X */
- X public int
- Xfind_linenum(pos)
- X POSITION pos;
- X{
- X register struct linenum *p;
- X register int lno;
- X register int loopcount;
- X POSITION cpos;
- X#if GET_TIME
- X long startime;
- X#endif
- X
- X if (!linenums)
- X /*
- X * We're not using line numbers.
- X */
- X return (0);
- X if (pos == NULL_POSITION)
- X /*
- X * Caller doesn't know what he's talking about.
- X */
- X return (0);
- X if (pos == (POSITION)0)
- X /*
- X * Beginning of file is always line number 1.
- X */
- X return (1);
- X
- X /*
- X * Find the entry nearest to the position we want.
- X */
- X for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
- X continue;
- X if (p->pos == pos)
- X /* Found it exactly. */
- X return (p->line);
- X
- X /*
- X * This is the (possibly) time-consuming part.
- X * We start at the line we just found and start
- X * reading the file forward or backward till we
- X * get to the place we want.
- X *
- X * First decide whether we should go forward from the
- X * previous one or backwards from the next one.
- X * The decision is based on which way involves
- X * traversing fewer bytes in the file.
- X */
- X flush();
- X#if GET_TIME
- X startime = get_time();
- X#endif
- X if (p == &anchor || pos - p->prev->pos < p->pos - pos)
- X {
- X /*
- X * Go forward.
- X */
- X p = p->prev;
- X if (ch_seek(p->pos))
- X return (0);
- X loopcount = 0;
- X for (lno = p->line, cpos = p->pos; cpos < pos; lno++)
- X {
- X /*
- X * Allow a signal to abort this loop.
- X */
- X cpos = forw_raw_line(cpos);
- X if (sigs || cpos == NULL_POSITION)
- X return (0);
- X#if GET_TIME
- X if (loopcount >= 0 && ++loopcount > 100)
- X {
- X loopcount = 0;
- X if (get_time() >= startime + LONGTIME)
- X {
- X longloopmessage();
- X loopcount = -1;
- X }
- X }
- X#else
- X if (loopcount >= 0 && ++loopcount > LONGLOOP)
- X {
- X longloopmessage();
- X loopcount = -1;
- X }
- X#endif
- X }
- X lnloop = 0;
- X /*
- X * If the given position is not at the start of a line,
- X * make sure we return the correct line number.
- X */
- X if (cpos > pos)
- X lno--;
- X } else
- X {
- X /*
- X * Go backward.
- X */
- X if (ch_seek(p->pos))
- X return (0);
- X loopcount = 0;
- X for (lno = p->line, cpos = p->pos; cpos > pos; lno--)
- X {
- X /*
- X * Allow a signal to abort this loop.
- X */
- X cpos = back_raw_line(cpos);
- X if (sigs || cpos == NULL_POSITION)
- X return (0);
- X#if GET_TIME
- X if (loopcount >= 0 && ++loopcount > 100)
- X {
- X loopcount = 0;
- X if (get_time() >= startime + LONGTIME)
- X {
- X longloopmessage();
- X loopcount = -1;
- X }
- X }
- X#else
- X if (loopcount >= 0 && ++loopcount > LONGLOOP)
- X {
- X longloopmessage();
- X loopcount = -1;
- X }
- X#endif
- X }
- X lnloop = 0;
- X }
- X
- X /*
- X * We might as well cache it.
- X */
- X add_lnum(lno, cpos);
- X return (lno);
- X}
- X
- X/*
- X * Return the line number of the "current" line.
- X * The argument "where" tells which line is to be considered
- X * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
- X */
- X public int
- Xcurrline(where)
- X int where;
- X{
- X POSITION pos;
- X
- X pos = position(where);
- X if (pos == NULL_POSITION)
- X pos = ch_length();
- X return (find_linenum(pos));
- X}
- X
- X#if DEBUG_STUFF
- Xdebug()
- X{
- X register struct linenum *p;
- X char buf[20];
- X
- X lower_left();
- X clear_eol();
- X for (p = anchor.next; p != &anchor; p = p->next)
- X {
- X sprintf(buf, "%d-%d ", p->line, p->pos);
- X putstr(buf);
- X }
- X putstr("\n");
- X error("DEBUG");
- X}
- X#endif /*DEBUG_STUFF*/
- END_OF_FILE
-
-
-